package com.amex.uam.service.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import com.amex.uam.dao.MstrDao;
import com.amex.uam.model.MSLogin;
import com.amex.uam.model.Message;
import com.amex.uam.model.SecurityGroupBean;
import com.amex.uam.service.MstrService;
import com.amex.uam.util.Utils;
import com.microstrategy.web.beans.ObjectBean;
import com.microstrategy.web.beans.UserGroupBean;
import com.microstrategy.web.objects.WebAccessControlEntry;
import com.microstrategy.web.objects.WebAccessControlList;
import com.microstrategy.web.objects.WebFolder;
import com.microstrategy.web.objects.WebIServerSession;
import com.microstrategy.web.objects.WebObjectSource;
import com.microstrategy.web.objects.admin.users.WebUser;
import com.microstrategy.web.objects.admin.users.WebUserGroup;
import com.microstrategy.webapi.EnumDSSXMLObjectFlags;

@Service("mstrService")
public class MstrServiceImpl implements MstrService {

	private MstrDao mstrDao;

	@Resource
	public void setMstrDao(MstrDao mstrDao) {
		this.mstrDao = mstrDao;
	}

	@Override
	public Message loadSecurityGroupByGroupName(String userId, String groupName, WebIServerSession session) {
		Message msg = null;
		
		if (session == null /*|| userId == null || userId.length() == 0*/
				|| groupName == null || groupName.length() == 0) {
			msg = new Message(Message.ERROR_PARAM, "The parameters are invalid.");
			
		} else {

			SecurityGroupBean group = mstrDao.getGroup(groupName, session);
			// List<SecurityGroupBean> userGroupList = mstrDao.getUserGroups(session); // get admin's groups
			if (userId != null && userId.length() > 0) {
				List<SecurityGroupBean> userGroupList = mstrDao.getUserGroups(userId, session);// get user's groups
				Set<String> idSet = new HashSet<String>();
				for (Iterator<SecurityGroupBean> it = userGroupList.iterator(); it.hasNext();) {
					SecurityGroupBean bean = (SecurityGroupBean) it.next();
					idSet.add(bean.getGroupID());
				}
				if (groupName.equals("MARC")) {
					markUserGroup(group, idSet);
					group.setGroupName(groupName);
				} else {
					markUserGroup(group, idSet);
				}
			}

			msg = new Message(Message.OK, group);

		}
		return msg;
	}

	private static void markUserGroup(SecurityGroupBean group, Set<String> idSet) {
		if (idSet.contains(group.getGroupID())) {
			group.setContains(true);
		} else {
			group.setContains(false);
		}
		for (Iterator<SecurityGroupBean> it = group.getChildren().iterator(); it
				.hasNext();) {
			SecurityGroupBean bean = (SecurityGroupBean) it.next();
			markUserGroup(bean, idSet);
		}
	}
	
	@Override
	public Message updateUserGroup(String userName, String originalVal, String modifiedVal, WebIServerSession session) {
		
		if (userName == null || userName.length() == 0) 
			return new Message(Message.ERROR_PARAM, "userName is invalid.");
		if (session == null) 
			return new Message(Message.ERROR_PARAM, "session is invalid.");
		if (modifiedVal == null) {
			if (originalVal != null) {
				this.mstrDao.removeUserFromGroup(userName, originalVal, session);
			}
			return new Message(Message.OK); // do nothing
		} 
		else if(modifiedVal.equals(originalVal)) {
			this.mstrDao.attachUserToGroup(userName, modifiedVal, session);
		}
		else if (originalVal != null) {
			this.mstrDao.removeUserFromGroup(userName, originalVal, session);
		}
		this.mstrDao.attachUserToGroup(userName, modifiedVal, session);
		
		return new Message(Message.OK);
	}
	
	@Override
	public Message updateSharedReports(String userName, String flag,
			String originalVal, Set<String> addList, WebIServerSession session) {
	
		if (session == null) 
			return new Message(Message.ERROR_PARAM, "session is invalid.");
		if (userName == null || userName.length() == 0) 
			return new Message(Message.ERROR_PARAM, "userName is invalid.");
		if (flag == null || (!"yes".equals(flag) && !"no".equals(flag))) 
			return new Message(Message.ERROR_PARAM, "flag is invalid.(please enter yes or no)");
		
		
		Set<String> mixedSet = new HashSet<String>();
		HashSet<String> removeList = new HashSet<String>();
		if (addList == null)
			addList = new HashSet<String>();
		
		if (originalVal != null) {
			String[] groups = originalVal.split(",");
			for (String groupName : groups) {
				if (groupName.trim().length() > 0) {
					if (mixedSet.contains(groupName))
						continue;
					if (addList.contains(groupName)) {
						addList.remove(groupName);
						mixedSet.add(groupName);
					} else {
						removeList.add(groupName);
					}
				}
			}
		}
		
		System.out.println("** add.list:");
		printSet(addList, "-->");
		System.out.println("** remove.list:");
		printSet(removeList, "-->");
		System.out.println("** mixed.list:");
		printSet(mixedSet, "-->");
		
		for (Iterator<String> it = addList.iterator(); it.hasNext();) {
			String groupName =  it.next();
			this.mstrDao.attachUserToGroup(userName, groupName, session);
		}
		for (Iterator<String> it = mixedSet.iterator(); it.hasNext();) {
			String groupName =  it.next();
			this.mstrDao.attachUserToGroup(userName, groupName, session);
		}
		for (Iterator<String> it = removeList.iterator(); it.hasNext();) {
			String groupName = it.next();
			this.mstrDao.removeUserFromGroup(userName, groupName, session);
		}
		
		return new Message(Message.OK);
	}
	
	@Override
	public Message createSharedReportGroup(String groupName, WebIServerSession session) {
		if (groupName == null || groupName.length() == 0) {
			return new Message(Message.ERROR, "Group name is required!");
		}
		if (session == null) {
			return new Message(Message.ERROR, "session is required");
		}
		try {
			
			// switch the project
			session.setProjectName("MicroStrategy Tutorial");//XXX hard code
			session.refresh();
			
			// build security group names
			String mainGroupName = "MARC " + groupName + " Security View";
			String rGroupName = "MARC " + groupName + " (Read Only)";
			String rwGroupName = "MARC " + groupName + " (Read/Write)";
			
			String[] groupNameList = {mainGroupName, rGroupName, rwGroupName};
			
			// check group name
			if (existGroup(groupNameList, session)) {
				return new Message(Message.ERROR, "The group \""+groupName+"\" already exist!");
			}
			
			// getRootFoler
			WebFolder rootFolder = mstrDao.getTeamSharedReportsFolder(session, "Team Shared Reports");
			if (rootFolder == null) {
				return Message.newError("Folder 'Team Shared Reports' not found");
			}
			
			// check folder name
			if (mstrDao.getSubFolder(session, rootFolder.getID(), groupName) != null) {
				return new Message(Message.ERROR, "The folder \""+groupName+"\" already exist!");
			}
			
			// create groups
			UserGroupBean mainGroup = mstrDao.createGroup(mainGroupName, session, false);
			if (mainGroup == null) return Message.newError("User group creation failed");
			UserGroupBean rGroup = mstrDao.createGroup(rGroupName, session, false);
			if (rGroup == null) return Message.newError("User group creation failed");
			UserGroupBean rwGroup = mstrDao.createGroup(rwGroupName, session, false);
			if (rwGroup == null) return Message.newError("User group creation failed");
			
			// assign relationships
			String parentGroupName = "MARC Shared Report Security View";//XXX hard code
			Message msg = mstrDao.addMemberGroup(parentGroupName, mainGroupName, session);
			if (msg.getStatus() != Message.OK) {
				return msg;
			}
			List<String> childList = new ArrayList<String>();
			childList.add(rGroupName);
			childList.add(rwGroupName);
			msg = mstrDao.addMemberGroups(mainGroupName, childList, session);
			if (msg.getStatus() != Message.OK) {
				return msg;
			}
			
			// create folder
			WebFolder newFolder = mstrDao.createFolder(rootFolder, groupName, session);
			if (newFolder == null) {
				return Message.newError("Folder "+groupName+" creation failed");
			}
			
			// remove all existed ACLs except the Administrator group
			WebAccessControlList list = newFolder.getSecurity().getACL();
			List<WebAccessControlEntry> removeList = new ArrayList<WebAccessControlEntry>();
			for (int i = 0; i < list.size(); i++) {
				WebAccessControlEntry entity = list.get(i);
				if (!"Administrator".equals(entity.getTrustee().getName())) {
					removeList.add(entity);
				}
			}
			
			for (int i = 0; i < removeList.size(); i++) {
				list.remove(removeList.get(i));
			}
			

			WebObjectSource source = session.getFactory().getObjectSource();
			source.setFlags(newFolder.getFlags() & ~EnumDSSXMLObjectFlags.DssXmlObjectDefn);
			source.save(newFolder);
			
			// assign ACL to the target folder
			
			boolean success = mstrDao.grantACLToSharedReportsFolder(rGroup.getObjectID(), newFolder.getID(), session);
			if (!success) {
				return Message.newError("ACL assign failed");
			}
			success = mstrDao.grantACLToSharedReportsFolder(rwGroup.getObjectID(), newFolder.getID(), session);
			if (!success) {
				return Message.newError("ACL assign failed");
			}
			// MARC Developer
			WebUserGroup marcDevGroup = mstrDao.searchUserGroup(parentGroupName, session);
			if (marcDevGroup != null && "MARC Developer".equals(marcDevGroup.getName())) {
				success = mstrDao.grantACLToSharedReportsFolder(marcDevGroup.getID(), newFolder.getID(), session);
				if (!success) {
					return Message.newError("ACL assign failed");
				}
			}
			
			return Message.newOK();
			
		} catch(Throwable t) {
			t.printStackTrace();
			return new Message(Message.ERROR, t.getMessage());
		}
	}

	private boolean existGroup(String[] groupNameList, WebIServerSession session){
		for (String groupName : groupNameList) {
			if (mstrDao.searchUserGroup(groupName, session)!= null){
				return true;
			}
		}
		return false;
	}
	
	@SuppressWarnings("rawtypes")
	private void printSet(Set<String> set, String prefix) {
		for (Iterator it = set.iterator(); it.hasNext();) {
			String item = (String) it.next();
			if (prefix == null) prefix = "";
			System.out.println(prefix + item);
		}
	}

	@Override
	public Message activateUser(String userIds, String activate, WebIServerSession session) {
		Message msg = new Message();
		if (userIds == null || userIds.trim().length() == 0) {
			return new Message(Message.ERROR_PARAM, "userIds is invalid.");
		}
		boolean isActivate;
		try {
			isActivate = Boolean.parseBoolean(activate);
		} catch(Throwable t){
			return new Message(Message.ERROR_PARAM, "user status is invalid.");
		}
		
		String[] idList = userIds.split(",");
		for (String userId : idList) {
			if (userId.trim().length() == 0) {
				continue;
			}
			mstrDao.activateUser(userId, isActivate, session);
		}
		
		return new Message(Message.OK);
		
	}
	
	@Override
	public Message removeUser(String userIds, WebIServerSession session) {
		if (userIds == null || userIds.trim().length() == 0) {
			return new Message(Message.ERROR_PARAM, "userIds is invalid.");
		}
		
		String[] idList = userIds.split(",");
		for (String userId : idList) {
			if (userId.trim().length() == 0) {
				continue;
			}
			mstrDao.removeUser(userId, session);
		}
		
		return new Message(Message.OK);
	}

	@Override
	public Message setAccessEndDate(String userId, String accEndDate, WebIServerSession session) {
		// validate the paramters
		if (session == null) 
			return new Message(Message.ERROR_PARAM, "session is invalid.");
		if (userId == null || userId.length() == 0) 
			return new Message(Message.ERROR_PARAM, "userId is invalid: " + userId);
		Date date = Utils.parseDate(accEndDate); 
		if (date == null) 
			return new Message(Message.ERROR_PARAM, "accEndDate is invalid: " + accEndDate);
		
		// handle the request
		boolean success = mstrDao.setPasswordExpirationDate(userId, date , session);
		
		// return the result
		if (success){
			return Message.newOK();
		} else {
			return Message.newError();
		}
	}

	@Override
	public WebIServerSession getWebIServerSession(MSLogin user) {
		if (user == null) {
			return null;
		}
		return mstrDao.getAdminSession(user);
	}
	
/*	@Override
	public void grantDenyACL(String userId, String optBlueOri,
			String optBlueVal, WebIServerSession session) {
		// TODO Auto-generated method stub
		
	}*/
}
